Pod 申請 nvidia.com/gpu: 1,只代表需要一個可分配的 GPU 資源,沒有表達 GPU 型號、VRAM 等級、機房位置或本機儲存需求。當叢集只有一種 GPU 時,這個限制不明顯;節點規格開始分化後,排程成功不再代表工作負載進入合適的節點。
Labels、nodeSelector 與 node affinity 的工作,就是把節點能力和工作負載需求連接起來。
Label 是附加在 Kubernetes 物件上的 key-value 資訊。套用在 Node 時,可以描述排程需要知道的特徵,例如:
自訂 label 最好使用組織擁有的 DNS prefix,避免和平台或第三方元件使用的 key 衝突。例如:
platform.example.com/accelerator-class=l40s
platform.example.com/vram-tier=large
platform.example.com/workload=inference
Label 應描述相對穩定且可驗證的事實。GPU utilization、目前空閒 VRAM、queue depth 等即時數值不適合由人工持續改寫成 label;變更頻率過高不但容易產生漂移,也會把排程決策建立在過期狀態上。
nodeSelector 是最直接的節點選擇方式。Pod 只會被排到同時符合所有指定 label 的節點。
apiVersion: v1
kind: Pod
metadata:
name: inference-worker
spec:
nodeSelector:
platform.example.com/workload: inference
platform.example.com/vram-tier: large
containers:
- name: server
image: example/inference-server:1.0
resources:
limits:
nvidia.com/gpu: 1
這種寫法容易閱讀,適合條件少而且都是必要限制的情況。缺點是只能做簡單的等值比對,也無法表達「A 或 B」或「優先選 A,沒有 A 也可接受 B」。
Node affinity 提供較完整的選擇語法,最重要的是能分成 hard requirement 與 soft preference。
requiredDuringSchedulingIgnoredDuringExecution 是必要條件。沒有符合節點時,Pod 會停在 Pending:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: platform.example.com/workload
operator: In
values: ["inference"]
- key: platform.example.com/vram-tier
operator: In
values: ["medium", "large"]
同一個 nodeSelectorTerm 內的 expressions 採 AND;多個 terms 之間採 OR。這個細節若理解錯誤,可能讓可選節點比預期更多或更少。
preferredDuringSchedulingIgnoredDuringExecution 則是偏好。Scheduler 會把符合規則的權重加入節點分數,但沒有符合節點時仍可排到別處:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: platform.example.com/local-model-cache
operator: In
values: ["ready"]
模型 cache、本機磁碟或較低成本節點通常適合做偏好;驅動相容性、必要 VRAM 等級與法規要求則應做硬性條件。
直接要求某個精確 GPU 型號很方便,但會讓 Deployment 與基礎設施緊密耦合。叢集新增可相容的新型號後,每個工作負載都要修改 manifest,維護成本會快速增加。
較穩定的做法是先定義能力層級,例如 vram-tier=large、accelerator-class=inference,再由平台團隊維護各節點屬於哪個等級。只有當核心功能確實依賴特定架構、指令集或測試認證時,才直接指定產品型號。
抽象 label 也不能只靠命名。每個等級都要有清楚定義,例如最低 VRAM、支援的 driver、允許的工作負載與效能基準,否則不同管理者會套用不同標準。
IgnoredDuringExecution 容易被忽略名稱中的 IgnoredDuringExecution 表示規則主要在排程當下生效。Pod 已經進入節點後,即使 label 被移除或改值,現有 Pod 通常不會因此自動搬移。
因此,修正錯誤 label 只能避免新的錯誤 placement,不能取代對既有 Pod 的盤點與重建。節點能力若發生重大變化,應搭配 cordon、drain、重新部署或自動化控制器處理。
工作負載越依賴 label,label 的正確性就越接近控制面的一部分。至少要建立以下規則:
若 label 會把高權限工作負載導向特定節點,不應讓不受信任的 kubelet 任意修改。Kubernetes 提供 NodeRestriction admission plugin 與受保護的 label prefix,可降低節點自行偽造隔離屬性的風險。
套用規則後,不要只確認 Pod 進入 Running。驗證至少包含:
硬性條件過多會降低可排程性;偏好過多則可能互相稀釋,讓結果難以理解。每一條規則都應能回答:不符合時,是必須拒絕,還是可以降級?
Label 負責描述節點能力,nodeSelector 適合簡單硬限制,node affinity 則能表達集合、否定、存在性與加權偏好。真正可維護的設計不在於寫出最複雜的規則,而是把必要條件和最佳化偏好分開,並讓 label 有明確來源、定義與維護責任。
把工作負載送到合適的 GPU 節點後,下一個問題是如何避免一般 Pod 佔用昂貴資源。下一篇將處理 Taints 與 Tolerations。